﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonLib {
    public class Tracer {
        private Screen screen;
        private Scene scene;

        private Ray _ray;
        private Vector3 _color, _lightDir, _primNormal, _insecPoint;
        public Tracer(Screen scr, Scene sce) {
            this.screen = scr;
            this.scene = sce;

            this._ray = new Ray(new Vector3(), new Vector3(1, 0, 0));
            this._color = new Vector3();
            this._lightDir = new Vector3();
            this._primNormal = new Vector3();
            this._insecPoint = new Vector3();
        }
        public Screen Screen {
            get { return this.screen; }
        }
        public Primitive findNearest(Ray ray, ref double distance) {
            Primitive nearest = null;

            for (int j = 0; j < scene.CountPrimitive(); j++) {
                Primitive tmp = scene.GetPrimitive(j);
                if (tmp.isIntersect(ray, ref distance) == true)
                    nearest = tmp;
            }
            return nearest;
        }
        public void seasonDiffuse(Primitive nrst, Vector3 bndry, Vector3 cffcnt) {
            for (int j = 0; j < scene.CountPrimitive(); j++) {
                Primitive light = scene.GetPrimitive(j);
                if (light.isLight() == false)
                    continue;
                light.assignNormal(bndry, _lightDir);
                nrst.assignNormal(bndry, _primNormal);
                double arg = -_primNormal.dot(_lightDir);
                if ((0 < arg) == false)
                    continue;
                cffcnt.mac(nrst.Material.Diffuse * arg, light.Material.Color);
            }
        }
        public void raytrace() {
            for (int i = 0; i < screen.getSize(); i++) {
                Primitive nearest = null;
                double distance = Double.MaxValue;

                screen.assignRay(i, _ray);
                nearest = findNearest(_ray, ref distance);

                _color.set(0);
                if (nearest == null) {
                    screen.setPixel(i, _color);
                    continue;
                }
                if (nearest.isLight()) {
                    _color.set(nearest.Material.Color);
                    screen.setPixel(i, _color);
                    continue;
                }

                _insecPoint.set(_ray.Dir).mul(distance).add(_ray.Eye); 
                seasonDiffuse(nearest, _insecPoint, _color);
                _color.mul(nearest.Material.Color);
                screen.setPixel(i, _color);
            }
        }
    }
}
